package sun.scheduler.core.job;

import java.util.ArrayList;
import sun.scheduler.core.resource.Resource;

/**
 * Stores all information not relatet to the calculations, such as the id and
 * the name of the job. This class basicly represents the description of a job.
 *
 * @author mfj
 */
public class Job
{

    /**
     * External Id of this job (jobId = lfd. Nr.)
     */
    public final int jobId;
    /**
     * External Id of the group (Gruppenummer)
     */
    public final int groupId;
    /**
     * The name of this job.
     */
    public final String jobName;
    /**
     * The name of the group which this job belongs to
     */
    public final String groupName;
    /**
     * Min, max and initial resources
     */
    public final double minResources, maxResources, initialResources;
    /**
     * maximal and minimal time this job can last
     */
    public final long minDuration, maxDuration;
    /**
     * Id of the resource used by this job
     */
    public final Resource resource;
    /**
     * Work of this job
     */
    public final int work;
    /**
     * {@code LTCTInfo}s used for the first time cost curve
     */
    private LTCTInfo LTCTInfo;
    /**
     * {@code RoundingInfo}s used for the second curve
     */
    private RoundingInfo roundingInfo;
    /**
     * {@code SchedulingInfo}s used for the third curve
     */
    private SchedulingInfo schedulingInfo;
    /**
     * List of predecessors
     */
    private ArrayList<Integer> predecessors, parallelJobs, successors;

    /**
     * Instantiates a new {@link JobInfo} class.
     *
     * @param jobId the unique id of the job.
     * @param groupId the group id of the job.
     * @param jobName the name of this job.
     * @param groupName the groubname of this job.
     */
    public Job(int jobId, int groupId, String jobName, String groupName,
            double minResources, double maxResources, double initialResources,
            int work, Resource resource)
    {
        this.jobId = jobId;
        this.groupId = groupId;
        this.jobName = jobName;
        this.groupName = groupName;
        this.predecessors = new ArrayList<Integer>();
        this.successors = new ArrayList<Integer>();
        this.parallelJobs = new ArrayList<Integer>();
        this.maxResources = maxResources;
        this.initialResources = initialResources;
        this.minResources = minResources;
        this.work = work;
        this.resource = resource;
        this.minDuration = (long) Math.floor(durationOfResources(maxResources));
        this.maxDuration = (long) Math.floor(durationOfResources(minResources));
        this.LTCTInfo = new LTCTInfo(this);
        this.roundingInfo = new RoundingInfo(LTCTInfo);
        this.schedulingInfo = new SchedulingInfo(roundingInfo);

    }

    /**
     * Adds a predecessor to the Job
     *
     * @param predecessor the {@code int} id of the predecessor
     */
    public void addPredecessor(int predecessor)
    {
        predecessors.add(predecessor);
    }

    /**
     * returns an list of the Predecessors
     *
     * @return {@code ArrayList<Integer>} predecessors
     */
    public ArrayList<Integer> getPredecessors()
    {
        return predecessors;
    }

    /**
     * Adds a successor to the job
     *
     * @param successor jobId of the successor
     */
    public void addSuccessor(int successor)
    {
        successors.add(successor);
    }

    /**
     * Returns the list of successor ids
     *
     * @return {@code ArrayList<Integer>} successors
     */
    public ArrayList<Integer> getSuccessors()
    {
        return successors;
    }

    /**
     * Adds a jobId which should run parallel to this one
     *
     * @param jobId of the parallel job
     */
    public void addParallelJob(int jobId)
    {
        parallelJobs.add(jobId);
    }

    /**
     * returns a list of jobIds which should run parallel to this job
     *
     * @return {@code ArrayList<Integer>} parallel jobs
     */
    public ArrayList<Integer> getPrallelJobs()
    {
        return parallelJobs;
    }

    /**
     * Returns the actual {@code IInfo} object due to the state
     *
     * @param cState specifies which kind of info is returned
     * @return instance of {@code LTCTInfo}, {@code RoundingInfo} or {@code SchedulingInfo}
     * according to cState
     */
    public Process getProcess(CurveState cState)
    {
        switch (cState)
        {
            case LTCT:
                return LTCTInfo;
            case Rounding:
                return roundingInfo;
            case Scheduling:
                return schedulingInfo;
            default:
                throw new IllegalStateException("State is invalid, due to curveState variable: " + cState);
        }
    }

    /**
     * sets the earliest start time according to the state variables of one of
     * the three curves
     *
     * @param est earliest start time to be set
     */
    public void setEarliestStartTime(CurveState cState, long est)
    {
        getProcess(cState).setEarliestStartTime(est);
    }

    /**
     * sets the latest end time according to the state variables of one of the
     * three curves
     *
     * @param let latest end time to be set
     */
    public void setLatestEndTime(CurveState cState, long let)
    {
        getProcess(cState).setLatestEndTime(let);
    }

    /**
     * gets the earliest start time
     *
     * @return est
     */
    public long getEarliestStartTime(CurveState cState)
    {
        return getProcess(cState).getEarliestStartTime();
    }

    /**
     * gets the latest end time
     *
     * @return latest end time
     */
    public long getLatestEndTime(CurveState cState)
    {
        return getProcess(cState).getLatestEndTime();
    }

    /**
     * Gets the duration of this job at the given time.
     *
     * @param cState
     * @return
     */
    public long getDuration(CurveState cState)
    {
        return getProcess(cState).getDuration();
    }

    /**
     * Gets the amount of used resources of the job at the given time.
     *
     * @param cState
     * @return
     */
    public double getAmountOfResources(CurveState cState)
    {
        return getProcess(cState).getAmountOfResources();
    }

    @Override
    public String toString()
    {
        return "ID: " + jobId + " Duration: "
                + getDuration(CurveState.Scheduling) + " EST/LET: " + getEarliestStartTime(CurveState.Scheduling) + "/"
                + getLatestEndTime(CurveState.Scheduling);
    }

    /**
     * Computes the duration of the given amount of resources.
     *
     * @param resources the amount of used resources.
     * @return the duration of the job.
     */
    public final double durationOfResources(double resources)
    {
        double exponent = Math.log((resources / initialResources)) / Math.log(2);
        return Math.pow(resource.aspectRatio, exponent) * (work / initialResources);
    }

    /**
     * Gets the total cost spend by this job at the given state of the
     * optimization.
     *
     * @param cState the state of the job to check
     * @return {@code resource.costPPT * getAmountOfResources(cState) * getDuration(cState)}
     */
    public double getCost(CurveState cState)
    {
        return resource.costPPT * getAmountOfResources(cState) * getDuration(cState) / 60;
    }

    /**
     * The State of the computation.
     */
    public enum CurveState
    {

        LTCT, Rounding, Scheduling
    }
}
